package it.fuzzylogics.fuzzywidgetsLibrary.client.FuzzyResizableCell; /*****************************************************************************
 *  Copyright [2008] [fuzzylogics]                                            *
 *                                                                            *
 *  Licensed under the Apache License, Version 2.0 (the "License");           *
 *  you may not use this file except in compliance with the License.          *
 *  You may obtain a copy of the License at                                   *
 *                                                                            *
 *      http://www.apache.org/licenses/LICENSE-2.0                            *
 *                                                                            *
 *  Unless required by applicable law or agreed to in writing, software       *
 *  distributed under the License is distributed on an "AS IS" BASIS,         *
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  *
 *  See the License for the specific language governing permissions and       *
 *  limitations under the License.                                            *
 ******************************************************************************/


import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.ui.ComplexPanel;
import com.google.gwt.user.client.ui.Widget;

import java.util.Vector;


public class FResizableCellPanel extends ComplexPanel {

    private int colnum;
    private int rownum;
    private Vector widgets;

    /**
     * Creates a FResizebleCellPanel with a specified number of columns and rows of the grid.
     *
     * @param colnum the number of columns
     * @param rownum the number of rows
     * @author FuzzyLogics
     */

    public FResizableCellPanel(int colnum, int rownum) {
        setElement(DOM.createDiv());
        DOM.setStyleAttribute(getElement(), "overflow", "hidden");
        this.colnum = colnum;
        this.rownum = rownum;
        widgets = new Vector();


    }

    /**
     * Sets the size of the FResizebleCellPanel object in pixel.
     *
     * @param width  width
     * @param height height
     */

    public void setPixelSize(int width, int height) {
        super.setPixelSize(width, height);

        updateWidgets(width, height);

    }

    /**
     * Inserts the specified widget at the position: (colIndex,rowIndex) laying out on numCols colums and numRows rows.
     *
     * @param widget   the specified widget
     * @param colIndex colIndex
     * @param rowIndex rowIndex
     * @param numCols  numCols
     * @param numRows  numRows
     */

    public void insertAt(Widget widget, double colIndex, double rowIndex, double numCols, double numRows) {
        double realX = colIndex * getOffsetWidth() / colnum;
        double realY = rowIndex * getOffsetHeight() / rownum;

        FCell fCell = new FCell();
        fCell.setWidget(widget);
        fCell.setColIndex(colIndex);
        fCell.setRowIndex(rowIndex);
        fCell.setNumCols(numCols);
        fCell.setNumRows(numRows);

        widgets.add(fCell);

        add(widget, (int) realX, (int) realY);
        fCell.getWidget().setPixelSize((int) (getOffsetWidth() * fCell.getNumCols() / colnum), (int) (getOffsetHeight() * fCell.getNumRows() / rownum));
        // updateWidgets(getOffsetWidth(), getOffsetHeight());

    }

    public void setWidgetAt(Widget widget, double colIndex, double rowIndex) {
        for (int i = 0; i < widgets.size(); i++)
            if (((FCell) widgets.get(i)).getWidget() == widget) {
                FCell fCell = (FCell) widgets.get(i);
                fCell.setColIndex(colIndex);
                fCell.setRowIndex(rowIndex);
                int realX = (int) (colIndex * getOffsetWidth() / colnum);
                int realY = (int) (rowIndex * getOffsetHeight() / rownum);
                setWidgetPosition(widget, realX, realY);
                break;
            }
    }


    public void setWidgetAtWithAnimation(Widget widget, double colIndex, double rowIndex, int time, Command finished, FStepExecute fstepExecute) {
        double oldColIndex;
        double oldRowIndex;


        for (int i = 0; i < widgets.size(); i++)
            if (((FCell) widgets.get(i)).getWidget() == widget) {
                FCell fCell = (FCell) widgets.get(i);
                oldColIndex = fCell.getColIndex();
                oldRowIndex = fCell.getRowIndex();

                fCell.setColIndex(colIndex);
                fCell.setRowIndex(rowIndex);
                moveWidget(widget, oldColIndex, oldRowIndex, colIndex, rowIndex, time, finished, fstepExecute);

                break;
            }
    }

    private void moveWidget(final Widget w, final double oldColIndex, final double oldRowIndex,
                            final double newColIndex, final double newRowIndex, int time, final Command finished, final FStepExecute fstepExecute) {


        Timer timer = new Timer() {
            int step = 4;

            public void run() {


                if (step <= 100) {
                    double curColIndex = oldColIndex + (newColIndex - oldColIndex) * step / 100;
                    double curRowIndex = oldRowIndex + (newRowIndex - oldRowIndex) * step / 100;
                    double realX = curColIndex * getOffsetWidth() / colnum;
                    double realY = curRowIndex * getOffsetHeight() / rownum;
                    setWidgetPosition(w, (int) realX, (int) realY);
                    if (fstepExecute != null)
                        fstepExecute.execute(w, step);
                    step += 4;

                } else {
                    cancel();
                    if (finished != null)
                        finished.execute();
                }


            }
        };

        timer.scheduleRepeating(time * 10);


    }

    public void setWidgetGeometryWithAnimation(Widget widget, double colIndex, double rowIndex, double numCol, double numRow, int time, Command finished, FStepExecute fstepExecute) {
        double oldColIndex;
        double oldRowIndex;
        double oldColNum;
        double oldRowNum;


        for (int i = 0; i < widgets.size(); i++)
            if (((FCell) widgets.get(i)).getWidget() == widget) {
                FCell fCell = (FCell) widgets.get(i);
                oldColIndex = fCell.getColIndex();
                oldRowIndex = fCell.getRowIndex();
                oldColNum = fCell.getNumCols();
                oldRowNum = fCell.getNumRows();
                fCell.setColIndex(colIndex);
                fCell.setRowIndex(rowIndex);
                fCell.setNumCols(numCol);
                fCell.setNumRows(numRow);
                moveAnsResizeWidget(widget, oldColIndex, oldRowIndex, oldColNum, oldRowNum, colIndex, rowIndex, numCol, numRow, time, finished, fstepExecute);

                break;
            }
    }

    private void moveAnsResizeWidget(final Widget w, final double oldColIndex, final double oldRowIndex, final double oldNumCol, final double oldNumRow,
                                     final double newColIndex, final double newRowIndex, final double newNumCol, final double newNumRow, int time, final Command finished, final FStepExecute fstepExecute) {


        Timer timer = new Timer() {
            int step = 4;

            public void run() {


                if (step <= 100) {
                    double curColIndex = oldColIndex + (newColIndex - oldColIndex) * step / 100;
                    double curRowIndex = oldRowIndex + (newRowIndex - oldRowIndex) * step / 100;
                    double curColNum = oldNumCol + (newNumCol - oldNumCol) * step / 100;
                    double curRowNum = oldNumRow + (newNumRow - oldNumRow) * step / 100;

                    double realX = curColIndex * getOffsetWidth() / colnum;
                    double realY = curRowIndex * getOffsetHeight() / rownum;
                    setWidgetPosition(w, (int) realX, (int) realY);
                    w.setPixelSize((int) (getOffsetWidth() * curColNum / colnum), (int) (getOffsetHeight() * curRowNum / rownum));

                    if (fstepExecute != null)
                        fstepExecute.execute(w, step);
                    step += 4;

                } else {
                    cancel();
                    if (finished != null)
                        finished.execute();
                }


            }
        };

        timer.scheduleRepeating(time * 10);


    }


    /**
     * Sets the number of columns.
     */

    public void setColNum(int colnum) {
        this.colnum = colnum;


        updateWidgets(getOffsetWidth(), getOffsetHeight());

    }

    /**
     * Sets the number of rows.
     */

    public void setRowNum(int rownum) {
        this.rownum = rownum;

        updateWidgets(getOffsetWidth(), getOffsetHeight());
    }

    /**
     * Updates widgets position and size.
     */

    private void updateWidgets(int width, int height) {

        for (int i = 0; i < widgets.size(); i++) {
            FCell fCell = (FCell) widgets.get(i);
            int realX = (int) (fCell.getColIndex() * width / colnum);
            int realY = (int) (fCell.getRowIndex() * height / rownum);

            setWidgetPosition(fCell.getWidget(), realX, realY);
            fCell.getWidget().setPixelSize((int) (width * fCell.getNumCols() / colnum), (int) (height * fCell.getNumRows() / rownum));


        }
    }

    /**
     * Gets the widget at (colIndex,rowIndex).
     *
     * @return the widget at position (colIndex,rowIndex) or null if there is no widget at that position
     */

    public Widget getWidgetAt(double colIndex, double rowIndex) {

        for (int i = 0; i < widgets.size(); i++) {
            FCell fCell = (FCell) widgets.get(i);

            if (fCell.getColIndex() == colIndex && fCell.getRowIndex() == rowIndex)
                return fCell.getWidget();

        }
        return null;
    }


    /**
     * Gets the FCell at (colIndex,rowIndex).
     *
     * @return the FCell at position (colIndex,rowIndex) or null if there is no FCell at that position
     */

    public FCell getFCellAt(double col, double row) {

        for (int i = 0; i < widgets.size(); i++) {
            FCell fCell = (FCell) widgets.get(i);

            if (fCell.getColIndex() == col && fCell.getRowIndex() == row)
                return fCell;

        }
        return null;
    }

    public double getWidgetColIndex(Widget w) {

        for (int i = 0; i < widgets.size(); i++) {
            FCell fCell = (FCell) widgets.get(i);

            if (fCell.getWidget() == w)
                return fCell.getColIndex();

        }
        return -1;
    }

    public double getWidgetRowIndex(Widget w) {

        for (int i = 0; i < widgets.size(); i++) {
            FCell fCell = (FCell) widgets.get(i);

            if (fCell.getWidget() == w)
                return fCell.getRowIndex();

        }
        return -1;
    }


    /**
     * Removes the widget at (colIndex,rowIndex).
     *
     * @return true if the widget is removed or false otherwise
     */

    public boolean removeAt(double colIndex, double rowIndex) {
        for (int i = 0; i < widgets.size(); i++) {
            FCell fCell = (FCell) widgets.get(i);

            if (fCell.getColIndex() == colIndex && fCell.getRowIndex() == rowIndex) {
                fCell.getWidget().removeFromParent();
                widgets.remove(i);
                return true;
            }

        }

        return false;
    }

    /**
     * Removes the specified widget.
     *
     * @return true if the widget is removed or false otherwise
     */

    public boolean removeWidget(Widget widget) {
        for (int i = 0; i < widgets.size(); i++) {
            FCell fCell = (FCell) widgets.get(i);

            if (fCell.getWidget() == widget) {
                fCell.getWidget().removeFromParent();
                widgets.remove(i);
                return true;
            }

        }
        return false;
    }

    /**
     * Removes all widgets.
     */

    public void removeAll() {
        for (int i = 0; i < widgets.size(); i++) {
            FCell fCell = (FCell) widgets.get(i);

            fCell.getWidget().removeFromParent();


        }
        widgets.clear();
    }

    private void checkWidgetParent(Widget w) {
        if (w.getParent() != this) {
            throw new IllegalArgumentException(
                    "Widget must be a child of this panel.");
        }
    }

    private static void changeToStaticPositioning(Element elem) {
        DOM.setStyleAttribute(elem, "left", "");
        DOM.setStyleAttribute(elem, "top", "");
        DOM.setStyleAttribute(elem, "position", "");
    }

    public void add(Widget w) {
        super.add(w, getElement());
    }

    public void add(Widget w, int left, int top) {
        w.removeFromParent();
        setWidgetPositionImpl(w, left, top);
        add(w);
    }

    public boolean remove(Widget w) {
        boolean removed = super.remove(w);
        if (removed) {
            changeToStaticPositioning(w.getElement());
        }
        return removed;
    }

    public void setWidgetPosition(Widget w, int left, int top) {
        setWidgetPositionImpl(w, left, top);
    }

    private void setWidgetPositionImpl(Widget w, int left, int top) {
        Element h = w.getElement();

        DOM.setStyleAttribute(h, "position", "absolute");
        DOM.setStyleAttribute(h, "left", left + "px");
        DOM.setStyleAttribute(h, "top", top + "px");
    }


    public void startAnimation(final Animatable anim, final int clock, final double stepIncrement, final boolean ciclic) {
        Timer timer = new Timer() {
            double step = stepIncrement;


            private void performAction() {
                for (int i = 0; i < getWidgetCount(); i++) {

                    FCell fCell = (FCell) widgets.get(i);

                    XY xy = anim.getPosition(fCell.getWidget(), step);

                    if (xy.x == -1 && xy.y == -1)
                        continue;
                    if (xy.x == -2 && xy.y == -2) {

                        step -= stepIncrement;

                        return;
                    }

                    fCell.setRowIndex(xy.y);
                    fCell.setColIndex(xy.x);
                    int realX = (int) (xy.x * getOffsetWidth() / colnum);
                    int realY = (int) (xy.y * getOffsetHeight() / rownum);
                    setWidgetPosition(fCell.getWidget(), realX, realY);


                }

            }

            public void run() {

                if (step < 100) {

                    performAction();


                    step += stepIncrement;
                    schedule(clock);

                } else {
                    step = 100;
                    performAction();
                    if (ciclic) {

                        step = stepIncrement;
                        schedule(clock);
                    }

                    // else
                    //  ;


                }


            }
        };

        timer.schedule(1);

    }

}